import sys
sys.path.append(sys.path[0] + "/../interpreter")
from customExceptions import *
from commonRoutines import *

class callFrame:
	def __init__(self, name, previous):
		self.name = name
		self.returnedFrames = []
		self.retVal = None
		self.prev = previous
		self.lastLabel = None
		
	def setValue(self, value):
		self.retVal = value
		
	def setLabel(self, label):
		self.lastLabel = label
		
	def getName(self):
		return self.name	
		
	def getValue(self):
		if(self.retVal !=  None):
			return self.retVal
		else:
			raise GeneralException("Call frame \"" + self.name + "\" checked for value before evaluation\n")
			
	def getLabel(self):
			return self.lastLabel
	
	def callFunction(self, name):
		next = callFrame(name, self)
		self.returnedFrames.append(next)
		return next
	
	def retFunction(self, value):
		self.setValue(value)
		return self.prev
		
	def getLabels(self):
		labelList = []
		for fun in self.returnedFrames:
			labelList.append(fun.getLabel())
		return labelList
		
	def getLabelByName(self):
		labelList = []
		for fun in self.returnedFrames:
			if(fun.getName() == name):
				retList.append(fun.getLabel())
		return labelList	
		
	def getReturns(self):
		retList = []
		for fun in self.returnedFrames:
			retList.append(fun.getValue())
		return retList
	
	def getReturnByName(self, name):
		retList = []
		for fun in self.returnedFrames:
			if(fun.getName() == name):
				retList.append(fun.getValue())
		return retList
		
	def __str__(self):
		retval = "Frame: [" + self.name + "] called: ["
		for frame in self.returnedFrames:
			retval += frame.getName() + "="  + frame.getValue() + ", "
		retval += "returningTo: "
		if( isinstance(self.prev, callFrame)):
			retval += self.prev.getName()
		else:
			retval += "None" 
	

class function:
	def __init__(self, name, typ = "i32", specialCase = False):
		self.name = name
		self.typ = typ
		self.args = []
		self.sourceToAssm = {}
		self.lines = []
		self.regNum = 0
		self.currentLabel = 0
		self.functionIndex = 0
		self.special = specialCase
		self.scopes = []
		self.this = False
	
	def addArg(self, typ, name):
		self.sourceToAssm[name] = "%" + name
		self.args.append((typ, "%" + name))
		
	def checkForArg(self, astName):
#		sys.stderr.write("Args in  " + str(self.name) + ": " + str(self.sourceToAssm) + "\n")
		if(astName in self.sourceToAssm):
			return self.sourceToAssm[astName]
		else:
			return None
	
	def searchForInstructionById(self, idnum):
		for inst in self.lines:
			if(type(inst) != type("")):
				if(inst.getID() == idnum):
					return inst
		
		return None
	
	def setThisTrue(self):
		if(not self.special):
			self.this = True
	
	def getNextReg(self):
		reg = "%" + str(self.name) + str(self.regNum)
		self.regNum += 1
		return reg
		
	def getArgs(self):
		return self.args
		
	def getInputArgs(self):
		return self.sourceToAssm.keys()
		
	def getNextLabel(self):
		label = self.name + "_label_" + str(self.currentLabel)
		self.currentLabel += 1
		return label
		
	def addLLVMInst(self, inst, comment = None):
		if(comment != None):	
			inst.setComment(comment)
		self.lines.append(inst)
	
	def addLLVMLine(self, data, comment = None):
		if(comment == None):
			self.lines.append("\t" + data + "\n")
		else:
			self.lines.append("\t" + data + " ; " + comment +"\n")
		
	def addLLVMLabel(self, data, comment = None):
		if(comment == None):
			self.lines.append("\n" + data + ":\n")
		else:
			self.lines.append("\n" + data + ": ; " + comment +"\n" )
	
	def setIndex(self, index):
		self.functionIndex = index
		
	def getIndex(self):
		return self.functionIndex
	
	def getName(self):
		return self.name
	
	def getSpecial(self):
		return self.special
	
	def getArgCount(self):
		return len(self.args)
	
	def getFunctionBody(self):
		retStr = ""
			
		if(not self.special):
			retStr +=  "define " + str(self.typ) +" @" + str(self.name) + "(%eframe* %env "
			first = False
		else:
			retStr +=  "define " + str(self.typ) +" @" + str(self.name) + "("
			first = True
		 
		
		for t in self.args:
			if(not first):
				retStr += ","
			retStr += str(t[0]) + " " + str(t[1])
			first = False
		retStr += ") {\n"
			
		
		
		if(self.this):
			retStr += str(llvm_call("printerror", "void", None, 19))
		 	retStr += "unreachable\n"  
			retStr += "}\n"
			return retStr
		else:
			for i in self.lines:
				retStr += str(i)
			
			retStr +=  str(llvm_ret("2"))
			retStr += "}\n"
		
		
		return retStr
		
	def getMethodBody(self):
		retStr = ""
		if(self.special):
			return retStr
		
		retStr +=  "define " + str(self.typ) +" @" + str(self.name) + "_method(%eframe* %env, i32 %this "
		first = False
		for t in self.args:
			if(not first):
				retStr += ", "
			retStr += str(t[0]) + " " + str(t[1])
		retStr += ") {\n"

			
		for i in self.lines:
			retStr += str(i)
			
		retStr += str(llvm_ret("2"))
		retStr += "}\n"
		
		
		
		return retStr


class llvmHelper:
	def __init__(self, debug = False):
		self.textSeg = []
		self.currentRegister = 0
		self.currentLabel = 0
		self.currentLevel = callFrame("base", None)
		self.currentscope = 0
		self.scopelevel = -1
		self.debug = debug
		self.stringlist = []
		self.initstrings = ""
		self.strnum = 0
		self.scopelist = []
		self.lastLabel = None
		self.functlist = ["constructor"]
		self.Debug = debug
		self.memberlist = []
		self.membernum = 0
		self.LLVMFunctions = [function("main", "i32", True)]
		self.LLVMFunctionStack = [self.LLVMFunctions[0]]
		self.scopeDepth = 0
		self.lastScope = []
		self.memberNumbers = {}
		self.getMemberDataNumber("constructor")
		
# Debug Code ##########################################################################################
		
	def debugPrint(self, reg, marker = None):
		if(self.Debug):
			markerDict = {"a":1, "b":2, "c":3}
			if (marker != None):
				self.addLLVMLine("call void @printmarker(i32 "+ str(markerDict[marker]) +")")
			if type(reg) == type(0):
				self.addLLVMLine("call void @printvalue(i32 "+ str((reg<<2))+ ")")
			else:
				self.addLLVMLine("call void @printvalue(i32 "+ str(reg)+ ")")

	def debugPrintFloat(self, reg):
		if(self.Debug):
			self.addLLVMLine("call void @printfloat(float " +str(reg)+ ")")
		
	def debugPrintFloatPtr(self, reg):
		if(self.Debug):
			self.addLLVMLine("call void @printfloatfromptr(float* "+ str(reg) +")")
	
	def debugPrintStringPtr(self, reg):
		if(self.Debug):
			self.addLLVMLine("call void @printstrfromptr(i8* "+ str(reg) +")")	
	
# LLVM Generation Code #################################################################################

	def getNextReg(self):
		if(self.LLVMFunctionStack[0] != None):
			reg = self.LLVMFunctionStack[0].getNextReg()
		else:
			reg = "%r" + str(self.currentRegister)
			self.currentRegister += 1
		return reg
	
	def getNextLabel(self):
		if(self.LLVMFunctionStack[0] != None):
			label = self.LLVMFunctionStack[0].getNextLabel()
		else:
			label = "label_" + str(self.currentLabel)
			self.currentLabel += 1
		return label
	
	def getLastLabel(self):
		return self.lastLabel
	
	def addLLVMInst(self, inst, comment = None):
		if(comment != None):	
			inst.setComment(comment)
		self.textSeg.append(inst)
		self.LLVMFunctionStack[0].addLLVMInst(inst, comment)
	
	def addLLVMLine(self, data, comment = None):
		if(comment == None):
			self.textSeg.append("\t" + data + "\n")
		else:
			self.textSeg.append("\t" + data + " ; " + comment +"\n")
		
		self.LLVMFunctionStack[0].addLLVMLine(data, comment)
		
	def addLLVMLabel(self, data, comment = None):
		if(comment == None):
			self.textSeg.append("\n" + data + ":\n")
		else:
			self.textSeg.append("\n" + data + ": ; " + comment +"\n" )
		self.lastLabel = data
		
		self.LLVMFunctionStack[0].addLLVMLabel(data, comment)
	
	def descend(self, name):
#		sys.stderr.write("Descending ->  " + str(name) + "\n")
		self.currentLevel = self.currentLevel.callFunction(name)
		
	def setLabel(self, label):
		self.currentLevel.setLabel(label)
		
	def ascend(self, value):
#		sys.stderr.write("Ascending (" + str(self.currentLevel.getName()) + "): "+ str(value) +"\n")
		self.currentLevel = self.currentLevel.retFunction(value)
		
	def getReturns(self):
		return self.currentLevel.getReturns()
		
	def getLabels(self):
		return self.currentLevel.getLabels()

	# functions to track which function we're on in LLVM
	def enterFunction(self, name):
		
		for f in self.LLVMFunctions:
			if (f.getName() == name):
				self.LLVMFunctionStack.insert(0,f)
				return
		
		print "ERROR: Function ", name,  " NOT FOUND!!!"
	
	def exitFunction(self):
		self.LLVMFunctionStack.pop(0)
	
	def checkCurrentFunctionForArg(self, name):
#		sys.stderr.write("\nChecking for arg " + str(name) + " in function " + self.LLVMFunctionStack[0].getName() + "\n")
		return self.LLVMFunctionStack[0].checkForArg(name)
		
	def getCurrentFunctionArgs(self):
		return self.LLVMFunctionStack[0].getArgs()
		
	def getFunctionIndex(self, name):
		for f in self.LLVMFunctions:
			if (f.getName() == name):
				return f.getIndex()
			
		
	#needed for while
	# how to replace?
	def thisReferenced(self):
		return self.LLVMFunctionStack[0].setThisTrue()
				
	

# Member list code ################################################################

	#This method is called on object operations. Adds the name of the object being called to a list,
	#inherently mapping it to a number.
#	def addMemberData(self,name):
#		for mem in self.memberlist:
#			if(mem == name):
#				return
#		self.memberlist.append(name)

	def getMemberDataNumber(self,name):
		if(not (name in self.memberNumbers)):
			self.memberNumbers[name] = self.membernum;
			self.membernum+= 1
		return self.memberNumbers[name]
#		counter = 0
#		for mem in self.memberlist:
#			if(mem == name):
#				return counter
#		print "ERROR: Can't find " + name + " in the member list"

	def printMemberData(self):
		print "\nMember Data:"
		count = 0
		for f in self.memberlist:
			print " " + str(count) + " | " + str(f)
			count += 1
		print "\n ------------------------------------ \n"

# StringVal Code ####################################################################################

	#Adds the string to the string list in the helper if it does not already exist.
	#Then is translates the string into the llvm hex style and returns the llvm string name (ie @strcons_01s) 
	def addStringVal(self,string):
		for strr in self.stringlist:
			if(strr == string):
				return
		self.stringlist.append(string)
		interpstring = ""
		for char in string:
			interpstring += "\\"+ self.toHex(char)
		strName = "@strcons_" + str(self.strnum) + "s"
		self.initstrings += strName + " = internal constant ["+str(len(string)+1)+" x i8] c\""+interpstring+"\\00\" \n"
		self.strnum += 1
		return strName

	#convert string to hex
	def toHex(self,s):
    		lst = []
    		for ch in s:
        		hv = hex(ord(ch)).replace('0x', '')
        		if len(hv) == 1:
            			hv = '0'+hv
        		lst.append(hv)
    
    		return reduce(lambda x,y:x+y, lst)

	
	def checkNumArgs(self, num):
		if(len(self.getReturns()) != num):
			errorString = "Incorrect number of arguments in function "
			errorString += self.currentLevel.getName()
			errorString = ". Values found: "
			argValues = self.getReturns()
			for value in argValues:
				errorString += str(value) + " , "
			raise GeneralException(errorString)

# Declaration and Main code ###########################################################################
	
	def addExternalFunctionDeclarations(self, string):
		string += "declare void @printerror(i32) noreturn\n"
		string += "declare void @printvalue(i32)\n"
		string += "declare void @printfloat(float)\n"
		string += "declare void @printstring(i8*)\n"
		string += "declare void @printmarker(i32)\n"
		string += "declare void @printfloatfromptr(float*)\n"
		string += "declare i32 @storeVarInScope(i32, i32, i32, i32)\n"
		string += "declare i32 @getVarFromScope(i32, i32, i32)\n"
		string += "declare i32 @getVarFromSlots(i32, i32, i32)\n"
		string += "declare i32 @mutateSlotsVar(i32, i32, i32, i32)\n"
		string += "declare i32 @stringLength(i32)\n"
		string += "declare i32 @subString(i32, i32, i32)\n"
		string += "declare i32 @stringEq(i32, i32)\n"
		string += "declare i32 @stringLessThan(i32, i32)\n"
		string += "declare i32 @read_line()\n"
		string += "\n"
		return string

	
	def addTypeDeclarations(self, string):
		string += "%jsobject = type{i32, %slots*}\n"
		string += "%floatobj = type{i32, float}\n"
		string += "%eframe = type{%eframe*, i32, [ 0 x i32 ]}\n"
		string += "%valArray = type{i32, [ 0 x i32 ]}\n"
		string += "%closure = type{i32,i32, %eframe*}\n"
		string += "%slots = type{i32,i32,%slots*}\n"
		string += "%strobj = type{i32, %slots*, i8*}\n" 
		savedReg = self.getNextReg()
		string += "@empty_slots = global %slots undef\n"
		string += "@empty_scope = global %eframe undef\n"		
		string += "\n"
		return string
		
	def addFunctions(self, string):
		for f in self.LLVMFunctions:
			string += f.getFunctionBody()
			string += "\n"
			string += f.getMethodBody()
			string += "\n"
		string = self.getDispatch(string)
		return string
	
	def getTextSegment(self):
#		lastReg =  self.currentLevel.getReturns().pop()
#		self.addLLVMLine("call void @printvalue(i32 "+ str(lastReg) + ")")
		textSeg = ""
		textSeg = self.addExternalFunctionDeclarations(textSeg)
		textSeg = self.addTypeDeclarations(textSeg)
		textSeg += self.initstrings
		
		textSeg = self.addFunctions(textSeg)

		return  textSeg

	def getDispatch(self, string):
		self.addFunctionDef("dispatch", "i32", True)
		self.addArgToFunction("closure")
		self.addArgToFunction("vals", "%valArray*")
		
		self.enterFunction("dispatch")
		
		c = commonRoutines(self)
		
		nonClosureErrorLabel = self.getNextLabel()
		nonExistantClosureErrorLabel = self.getNextLabel()
		incorrectNumberOfArgsErrorLabel =self.getNextLabel() 
		
		cosureAsIntReg = self.getNextReg()
		
#		c.checkType("%closure_u", "pointer", errorLabel)
#		pointerType = c.loadObjType("%closure_u", "%closure*")
		c.checkType("%closure", "pointer", nonClosureErrorLabel)
		pointerType = c.loadObjType("%closure", "%closure*")
		c.checkType(pointerType, "closure", nonClosureErrorLabel)
#		closureIndex = c.loadClosureValue("%closure_u")
#		closurePointer = c.testAndUnmaskPointer("%closure_u", "%closure*")
		closureIndex = c.loadClosureValue("%closure")
		closurePointer = c.testAndUnmaskPointer("%closure", "%closure*")
		
		finalLabel = self.getNextLabel() 
		
		thisLabel = self.getNextLabel()
		nextLabel = self.getNextLabel()
		
		resultRegs = []
		
		firstFunct = True
		
		dispatchFunctions = []
		
		for f in self.LLVMFunctions: 
			if(not f.getSpecial()):
				dispatchFunctions.append(f)
				

		for f in dispatchFunctions:
				
			branchReg = self.getNextReg()
			pointerToEframeReg = self.getNextReg()
			valArrayCountAddrReg = self.getNextReg()
			valArrayCountReg = self.getNextReg()
			valArrayBranchReg = self.getNextReg()
			eframeReg = self.getNextReg() 
			result = self.getNextReg()
			
			correctArgNumLabel = self.getNextLabel()
			
			self.addLLVMLine(";Function: " + f.getName())
			
			self.addLLVMInst(llvm_icmp_eq(branchReg, f.getIndex(), closureIndex), "Checking for fuction " + f.getName())
			if(f.getName() != dispatchFunctions[-1].getName()):
				self.addLLVMInst(llvm_brc(branchReg, thisLabel, nextLabel), "Branch!")
			else:
				self.addLLVMInst(llvm_brc(branchReg, thisLabel, nonExistantClosureErrorLabel), "Branch!")
			self.addLLVMLabel(thisLabel, "Code to call function " + f.getName())
			

			self.addLLVMInst(llvm_gep(pointerToEframeReg, closurePointer, "%closure*", 0, "i32", 2), "Load env for function")
			self.addLLVMInst(llvm_load(eframeReg, pointerToEframeReg, "%eframe*" ))
			
			
#			self.addLLVMInst(llvm_gep(valArrayCountAddrReg, "%vals_u", "%valArray*", 0, "i32", 0), "Load addr for number of args")
			self.addLLVMInst(llvm_gep(valArrayCountAddrReg, "%vals", "%valArray*", 0, "i32", 0), "Load addr for number of args")

			self.addLLVMInst(llvm_load(valArrayCountReg, valArrayCountAddrReg), "load num of args")
			self.addLLVMInst(llvm_icmp_eq(valArrayBranchReg, f.getArgCount(), valArrayCountReg), "Ensuring  " + str(f.getArgCount()) + " is number of args")
			self.addLLVMInst(llvm_brc(valArrayBranchReg, correctArgNumLabel, incorrectNumberOfArgsErrorLabel), "Branch!")
			self.addLLVMLabel(correctArgNumLabel, "Correct number of args!")
			
			argList = []
			for arg in range(f.getArgCount()):
				pointerToThisArg = self.getNextReg()
				valueOfArg = self.getNextReg()
				
#				self.addLLVMInst(llvm_gep(pointerToThisArg, "%vals_u", "%valArray*", 0, "i32", 1, "i32", arg), "Load addr for " + str(arg) + "th argument")
				self.addLLVMInst(llvm_gep(pointerToThisArg, "%vals", "%valArray*", 0, "i32", 1, "i32", arg), "Load addr for " + str(arg) + "th argument")
				self.addLLVMInst(llvm_load(valueOfArg, pointerToThisArg))
				
				argList.append(valueOfArg)
				
			# figure out how to add an arbitrary number of arguments
			callInst = llvm_call(str(f.getName()), "i32", result)
			callInst.setSource(0, eframeReg)
			callInst.setType(1, "%eframe*")
			
			i = 1
			for arg in range(f.getArgCount()):
				callInst.setSource(i, argList[i-1])
				callInst.setType(i+1, "i32")
				i += 1
				
			self.addLLVMInst(callInst)
			
			tupleData = result, self.getLastLabel()
			resultRegs.append(tupleData)
			self.addLLVMInst(llvm_br(finalLabel), "Done! Let's GTFO")
			
			if(f.getName() != dispatchFunctions[-1].getName()):
				self.addLLVMLabel(nextLabel, "Not that function, continue on!")
			
			nextLabel = self.getNextLabel()
			thisLabel = self.getNextLabel()
			
			#figure out how to phi an arbirtrary number of args 
		
		self.addLLVMLabel(incorrectNumberOfArgsErrorLabel, "An error somehow!")
		c.signalError(30)
		self.addLLVMLabel(nonExistantClosureErrorLabel, "An error somehow!")
		c.signalError(31)
		self.addLLVMLabel(nonClosureErrorLabel, "An error somehow!")
		c.signalError(32)
		
		self.addLLVMLabel(finalLabel, "Oh man, let's get out")
		
		finalResult = self.getNextReg()
		phiText = str(finalResult) + " = phi i32 "
		for result, label in resultRegs:
			phiText += "[" + str(result) + ", %" +str(label)+"], "
		
		phiText = phiText[:-2]
		
		self.addLLVMLine(phiText)
		
		self.addLLVMInst(llvm_ret(finalResult))
		
		string += self.LLVMFunctions[0].getFunctionBody()
		
		self.exitFunction()
		
# METHOD DISPATCH ====================================================================================================
		self.addFunctionDef("method_dispatch", "i32", True)
		self.addArgToFunction("closure")
		self.addArgToFunction("this")
		self.addArgToFunction("vals", "%valArray*")
		
		self.enterFunction("method_dispatch")
		
		c = commonRoutines(self)
		
		nonClosureErrorLabel = self.getNextLabel()
		nonExistantClosureErrorLabel = self.getNextLabel()
		incorrectNumberOfArgsErrorLabel =self.getNextLabel() 
		
		cosureAsIntReg = self.getNextReg()
		
#		c.checkType("%closure_u", "pointer", errorLabel)
#		pointerType = c.loadObjType("%closure_u", "%closure*")
		c.checkType("%closure", "pointer", nonClosureErrorLabel)
		pointerType = c.loadObjType("%closure", "%closure*")
		c.checkType(pointerType, "closure", nonClosureErrorLabel)
#		closureIndex = c.loadClosureValue("%closure_u")
#		closurePointer = c.testAndUnmaskPointer("%closure_u", "%closure*")
		closureIndex = c.loadClosureValue("%closure")
		closurePointer = c.testAndUnmaskPointer("%closure", "%closure*")
		
		finalLabel = self.getNextLabel() 
		
		thisLabel = self.getNextLabel()
		nextLabel = self.getNextLabel()
		
		resultRegs = []
		
		firstFunct = True
				

		for f in dispatchFunctions:
				
			branchReg = self.getNextReg()
			pointerToEframeReg = self.getNextReg()
			valArrayCountAddrReg = self.getNextReg()
			valArrayCountReg = self.getNextReg()
			valArrayBranchReg = self.getNextReg()
			eframeReg = self.getNextReg() 
			result = self.getNextReg()
			
			correctArgNumLabel = self.getNextLabel()
			
			self.addLLVMInst(llvm_icmp_eq(branchReg, f.getIndex(), closureIndex), "Checking for fuction " + f.getName())
			if(f.getName() != dispatchFunctions[-1].getName()):
				self.addLLVMInst(llvm_brc(branchReg, thisLabel, nextLabel), "Branch!")
			else:
				self.addLLVMInst(llvm_brc(branchReg, thisLabel, nonExistantClosureErrorLabel), "Branch!")
			self.addLLVMLabel(thisLabel, "Code to call function " + f.getName())
			

			self.addLLVMInst(llvm_gep(pointerToEframeReg, closurePointer, "%closure*", 0, "i32", 2), "Load env for function")
			self.addLLVMInst(llvm_load(eframeReg, pointerToEframeReg, "%eframe*" ))
			
			
#			self.addLLVMInst(llvm_gep(valArrayCountAddrReg, "%vals_u", "%valArray*", 0, "i32", 0), "Load addr for number of args")
			self.addLLVMInst(llvm_gep(valArrayCountAddrReg, "%vals", "%valArray*", 0, "i32", 0), "Load addr for number of args")

			self.addLLVMInst(llvm_load(valArrayCountReg, valArrayCountAddrReg), "load num of args")
			self.addLLVMInst(llvm_icmp_eq(valArrayBranchReg, f.getArgCount(), valArrayCountReg), "Ensuring  " + str(f.getArgCount()) + " is number of args")
			self.addLLVMInst(llvm_brc(valArrayBranchReg, correctArgNumLabel, incorrectNumberOfArgsErrorLabel), "Branch!")
			self.addLLVMLabel(correctArgNumLabel, "Correct number of args!")
			
			argList = []
			for arg in range(f.getArgCount()):
				pointerToThisArg = self.getNextReg()
				valueOfArg = self.getNextReg()
				
#				self.addLLVMInst(llvm_gep(pointerToThisArg, "%vals_u", "%valArray*", 0, "i32", 1, "i32", arg), "Load addr for " + str(arg) + "th argument")
				self.addLLVMInst(llvm_gep(pointerToThisArg, "%vals", "%valArray*", 0, "i32", 1, "i32", arg), "Load addr for " + str(arg) + "th argument")
				self.addLLVMInst(llvm_load(valueOfArg, pointerToThisArg))
				
				argList.append(valueOfArg)
				
			# figure out how to add an arbitrary number of arguments
			callInst = llvm_call(str(f.getName())+"_method", "i32", result)
			callInst.setSource(0, eframeReg)
			callInst.setType(1, "%eframe*")
#			callInst.setSource(1, "%this_u")
			callInst.setSource(1, "%this")
			callInst.setType(2, "i32")
			
			
			i = 2
			for arg in range(f.getArgCount()):
				callInst.setSource(i, argList[i-2])
				callInst.setType(i+1, "i32")
				i += 1
				
			self.addLLVMInst(callInst)
			
			tupleData = result, self.getLastLabel()
			resultRegs.append(tupleData)
			self.addLLVMInst(llvm_br(finalLabel), "Done! Let's GTFO")
			
			if(f.getName() != dispatchFunctions[-1].getName()):
				self.addLLVMLabel(nextLabel, "Not that function, continue on!")
			
			nextLabel = self.getNextLabel()
			thisLabel = self.getNextLabel()
			
			#figure out how to phi an arbirtrary number of args
			

		
		self.addLLVMLabel(incorrectNumberOfArgsErrorLabel, "Incorrect number of args!")
		c.signalError(33)
		self.addLLVMLabel(nonExistantClosureErrorLabel, "No closure!")
		c.signalError(34)
		self.addLLVMLabel(nonClosureErrorLabel, "That's not a closure!")
		c.signalError(35)
		
		self.addLLVMLabel(finalLabel, "Oh man, let's get out")
		
		finalResult = self.getNextReg()
		phiText = str(finalResult) + " = phi i32 "
		for result, label in resultRegs:
			phiText += "[" + str(result) + ", %" +str(label)+"], "
		
		phiText = phiText[:-2]
		
		self.addLLVMLine(phiText)
		
		self.addLLVMInst(llvm_ret(finalResult))
		
		string += self.LLVMFunctions[0].getFunctionBody()
		
		self.exitFunction()

		
		return string


# Function Def Code ###############################################################################

	def addFunctionDef(self, name, typ = "i32", special = False):
#		sys.stderr.write("Adding function def!: " + str(name) + "\n")
		self.LLVMFunctions.insert(0,function(name, typ, special))
#		self.LLVMFunctions.insert(0,function(name + "_user", typ))
		self.LLVMFunctions[0].setIndex(len(self.functlist))
		self.functlist.append(name)
		

	def addArgToFunction(self, name, typ = "i32"):
		self.LLVMFunctions[0].addArg(typ, name)
		
	def getStaticPassArgs(self):
		return self.LLVMFunctions[0].getInputArgs()
	
	def getFunctionDef(self,name):
		count = 0
		for f in self.functlist:
			if(name == f):
				return count
			count += 1
		print "Function Definition not found"
		return -1

	def printFunctions(self):
		print "\nFunction definitions:"
		count = 0
		for f in self.functlist:
			print " " + str(count) + " | " + str(f)
			count += 1
		print "\n ------------------------------------ \n"
		
# Scope Code #########################################################################################	

	def addScope(self):
		self.scopelevel += 1
		var = VarTrack(self.currentscope,self.scopelevel)
		self.scopelist.append(var)
		self.currentscope = len(self.scopelist) - 1
		#print "Added new scope : " + str(self.currentscope)
	
	# get storage place of previous scope pointer
	def getCurrentScopeReg(self):
		if(len(self.lastScope) > 0):
			return self.lastScope[0]
		else:
			return "@empty_scope"
		
	def incrementScopeDepth(self, oldScopeReg):
		self.lastScope.insert(0,oldScopeReg)
		
	def decrementScopeDepth(self):
		self.lastScope.pop(0)

	def retScope(self):
		self.scopelevel -= 1
		c = self.currentscope
		ret = self.scopelist[c].getRet()
		self.currentscope = ret
		#print "Left scope " + str(c) + " and returned to scope "+ str(ret)

	def getVar(self, name, currentscope= None):
		if(name == "this"):
			self.thisReferenced()
			return "%this"
	
		scopedepth = 0
		if(currentscope != None):
			scopelook = currentscope
		else:
			scopelook = self.scopelist[self.currentscope]
		while(True):
			#print "Getting var: " + name + " in " + str(scopelook) + "\n"
			answer = scopelook.get(name)
			if(answer != None):
				answer = [answer[0], scopedepth, answer[2]]
				return answer
			if(scopelook == self.scopelist[0]):
				break
			scopelook = self.scopelist[scopelook.getRet()]
			scopedepth += 1


		return None
		

	def addVarToScope(self, name):
		#print "Added " + name + " to the scope "+ str(self.currentscope)
		#print "Length = " + str(len(self.scopelist))
		self.scopelist[self.currentscope].add(name)

	def addVarToPreviousScope(self, name):
		if(self.currentscope != 0):
			self.scopelist[self.currentscope - 1].add(name)
		else:
			self.scopelist[self.currentscope].add(name)

	def checkFunctionArgs(self, name):
		if(self.LLVMFunctions[0].name == name):
			pass
			

	#when this method is called it return the varTrack of the current scope being used
	#call the get method on that vartrack with the name of the var to get the scope information
	def getCurrentScope(self):
		var = self.scopelist[self.currentscope]
		return var

	def displayScope(self):
		counter = 0
		#print "Length of scope list = " + str(len(self.scopelist))
		#print self.scopelist
		for e in self.scopelist:
			sys.stderr.write("Scope " + str(counter) + ":")
			if(e != None):
				sys.stderr.write(e.outputScopeVars())
			counter += 1
		sys.stderr.write("\n ------------------------------------ \n")


#This class a list of the variables in the scope
class VarTrack:
	def __init__(self, ret,scopenum):
		self.lst = []
		self.count = 0
		self.returnscope = ret
		self.scopenum = scopenum

	def add(self, name):
		#print "Adding to vartrack: "+ name
		self.lst.append([name, 0, self.count])
		self.count += 1

	def get(self,name):
		if(self.lst == None):
			return None
		self.lst.reverse()
		
		for i in self.lst:
			if(i[0] == name):
				self.lst.reverse()
				return i
		self.lst.reverse()

		return None
	
	def getScopeNum(self):
		return self.scopenum
		
	def getNumberOfVars(self):
		return self.count
	
	def getRet(self):
		return self.returnscope

	def __str__(self):
		return "Scopenumber: "+ str(self.scopenum)

	def outputScopeVars(self):
		if(len(self.lst) == 0):
			return "Empty"
		else:
			string = ""
			for i in self.lst:
				#if(i != None):
				string += str(i) + "\n"
			return string[:-1]

